﻿using System;
using System.Windows;
using Microsoft.AspNet.SignalR.Client;
using Microsoft.Practices.Prism.Logging;
using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Prism.PubSubEvents;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.Prism.UnityExtensions;
using Microsoft.Practices.ServiceLocation;
using Microsoft.Practices.Unity;
using Xceed.Wpf.AvalonDock;
using Hopscotch.Application.Shell.Login;
using Hopscotch.Application.Shell.Utils;
using Hopscotch.Presentation.Communication.HubClients;
using Hopscotch.Common.Communication.WcfClients;
using Hopscotch.Common.Contracts.AppService;
using Hopscotch.Common.Contracts.EnqService;
using Hopscotch.Common.Logging;
using Hopscotch.Common.Services.Wcf;
using Hopscotch.Presentation.Common;
using Hopscotch.Presentation.Common.Threading;
using Hopscotch.Presentation.Configuration;
using Hopscotch.Presentation.Communication;

namespace Hopscotch.Application.Shell
{

    public class Bootstrapper : UnityBootstrapper
    {
        RegionAdapterMappings mappings;
        private IEnvironmentConfiguration environmentConfiguration;
        private readonly ILogger logger;
        private PubSubEvent<MessageUpdateEvent> messageUpdateEvent;

        public Bootstrapper(IEventAggregator eventAggregator, IEnvironmentConfiguration environmentConfiguration)
        {
            this.environmentConfiguration = environmentConfiguration;
            this.messageUpdateEvent = eventAggregator.GetEvent<MessageUpdateEvent>();
            this.logger = AppLogger.CreateLogger<Bootstrapper>();
        }

        private void MessageUpdate(string message)
        {
            logger.Info(message);
            messageUpdateEvent.Publish(new MessageUpdateEvent() { Message = message });
        }

        protected override ILoggerFacade CreateLogger()
        {
            return PrismLogger.CreateLoggerFacade(typeof(ShellWindow));
        }

        protected override IModuleCatalog CreateModuleCatalog()
        {
            MessageUpdate("Creating Module Catalog");
            //DirectoryModuleCatalog catalog = new DirectoryModuleCatalog();
            //catalog.ModulePath = @".\Modules";

            ModuleCatalog catalog = new ConfigurationModuleCatalog();
            return catalog;
        }

        protected override void ConfigureContainer()
        {
            base.ConfigureContainer();

            //Register AppController
            var environmentKey = environmentConfiguration.CreateEnvironmentKey();
            IAppController appController = new AppController(ApplicationDispatcher.CreateFromCurrentThread(), environmentKey);
            this.Container.RegisterInstance<IAppController>(appController);

            //Register WCF proxys
            RegisterWcfClientProxy<ITradeService, TradeServiceClient>(this.Container);
            RegisterWcfClientProxy<IInstrumentService, InstrumentServiceClient>(this.Container);
            RegisterWcfClientProxy<ICurrencyInstrumentService, CurrencyInstrumentServiceClient>(this.Container);
        }

        protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
        {
            mappings = base.ConfigureRegionAdapterMappings();

            mappings.RegisterMapping(typeof(DockingManager), new AvalonDockRegionAdapter(ServiceLocator.Current.GetInstance<RegionBehaviorFactory>()));

            return mappings;
        }

        protected override DependencyObject CreateShell()
        {
            var shell = this.Container.Resolve(typeof(ShellWindow), null);
            return (ShellWindow)shell;
        }

        protected override void InitializeShell()
        {
            base.InitializeShell();

            InitializeServices();

            App.Current.MainWindow = (ShellWindow)this.Shell;
            App.Current.MainWindow.Show();
        }

        void InitializeServices()
        {
            MessageUpdate("Initializing Services");

            //Register SignalR hub proxys
            var address = environmentConfiguration.SignalRHubAddress;
            logger.Info("Connecting to {0} SignalR hub on {1}", environmentConfiguration.EnvironmentId, address);
            var hubConnection = new HubConnection(address);
            var enqConnection = new EnqConnection(hubConnection, this.Container.Resolve<IEventAggregator>());
            hubConnection.JsonSerializer.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto;

            hubConnection.TraceLevel = TraceLevels.All;
            hubConnection.TraceWriter = Console.Out;

            //Due to a limitation of SignalR, we need to create all the Hubs before the connection is started
            IHubProxy diagnosticsHub = hubConnection.CreateHubProxy(HubNames.DiagnosticsEnqHub);
            IHubProxy instrumentHub = hubConnection.CreateHubProxy(HubNames.InstrumentEnqHub);
            IHubProxy tradeHub = hubConnection.CreateHubProxy(HubNames.TradeEnqHub);

            //Wire-up the EnqHub services with their respective client wrappers
            this.Container.RegisterInstance(typeof(IDiagnosticsEnqService), new DiagnosticsHubClient(diagnosticsHub));

            this.Container.RegisterType<ITradeEnqService, TradeHubClient>(new ContainerControlledLifetimeManager(),
                new InjectionFactory(c => new TradeHubClient(enqConnection, tradeHub)));
            this.Container.RegisterType<ITradeEnqClient, TradeHubClient>();
            
            this.Container.RegisterType<IInstrumentEnqService, InstrumentHubClient>(new ContainerControlledLifetimeManager(),
                new InjectionFactory(c => new InstrumentHubClient(instrumentHub)));

            try
            {
                hubConnection.Start().Wait();
            }
            catch (Exception ex)
            {
                logger.Error("err", ex);
                throw;
            }
        }


        protected void RegisterWcfClientProxy<TFrom, TTo>(IUnityContainer container)
            where TTo : TFrom
        {
            string wcfClientConfigFile = environmentConfiguration.WcfClientConfigFile;

            container.RegisterType<IClientProxyFactory<TFrom>>(
                new ContainerControlledLifetimeManager(),
                new InjectionFactory(
                    (c) => new CustomChannelFactory<TFrom>(wcfClientConfigFile)));

            container.RegisterType<TFrom, TTo>();
        }

        protected void RegisterServiceProxy<T>(IUnityContainer container)
        {
            string wcfClientConfigFile = environmentConfiguration.WcfClientConfigFile;

            container.RegisterType<T>(
                new ContainerControlledLifetimeManager(),
                new InjectionFactory(
                    (c) => new CustomChannelFactory<T>(wcfClientConfigFile).CreateChannel()));
        }

    }
}
